{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4cbf2458",
   "metadata": {},
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-1/chain.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58238466-lesson-4-chain)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "ee55d3da-c53a-4c76-b46f-8e0d602e072e",
   "metadata": {},
   "source": [
    "# Chain\n",
    "\n",
    "## Review\n",
    "\n",
    "We built a simple graph with nodes, normal edges, and conditional edges.\n",
    "\n",
    "## Goals\n",
    "\n",
    "Now, let's build up to a simple chain that combines 4 [concepts](https://python.langchain.com/v0.2/docs/concepts/):\n",
    "\n",
    "* Using [chat messages](https://python.langchain.com/v0.2/docs/concepts/#messages) as our graph state\n",
    "* Using [chat models](https://python.langchain.com/v0.2/docs/concepts/#chat-models) in graph nodes\n",
    "* [Binding tools](https://python.langchain.com/v0.2/docs/concepts/#tools) to our chat model\n",
    "* [Executing tool calls](https://python.langchain.com/v0.2/docs/concepts/#functiontool-calling) in graph nodes \n",
    "\n",
    "![Screenshot 2024-08-21 at 9.24.03 AM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbab08dd607b08df5e1101_chain1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a55e2e80-a718-4aaf-99b9-371157b34a4b",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langchain_openai langchain_core langgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae5ac2d0-c7b0-4a20-86e5-4b6ed15ec20e",
   "metadata": {},
   "source": [
    "## Messages\n",
    "\n",
    "Chat models can use [`messages`](https://python.langchain.com/v0.2/docs/concepts/#messages), which capture different roles within a conversation. \n",
    "\n",
    "LangChain supports various message types, including `HumanMessage`, `AIMessage`, `SystemMessage`, and `ToolMessage`. \n",
    "\n",
    "These represent a message from the user, from chat model, for the chat model to instruct behavior, and from a tool call. \n",
    "\n",
    "Let's create a list of messages. \n",
    "\n",
    "Each message can be supplied with a few things:\n",
    "\n",
    "* `content` - content of the message\n",
    "* `name` - optionally, a message author \n",
    "* `response_metadata` - optionally, a dict of metadata (e.g., often populated by model provider for `AIMessages`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "866b5321-a238-4a9e-af9e-f11a131b5f11",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Name: Model\n",
      "\n",
      "So you said you were researching ocean mammals?\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "Name: Lance\n",
      "\n",
      "Yes, that's right.\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Name: Model\n",
      "\n",
      "Great, what would you like to learn about.\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "Name: Lance\n",
      "\n",
      "I want to learn about the best place to see Orcas in the US.\n"
     ]
    }
   ],
   "source": [
    "from pprint import pprint\n",
    "from langchain_core.messages import AIMessage, HumanMessage\n",
    "\n",
    "messages = [AIMessage(content=f\"So you said you were researching ocean mammals?\", name=\"Model\")]\n",
    "messages.append(HumanMessage(content=f\"Yes, that's right.\",name=\"Lance\"))\n",
    "messages.append(AIMessage(content=f\"Great, what would you like to learn about.\", name=\"Model\"))\n",
    "messages.append(HumanMessage(content=f\"I want to learn about the best place to see Orcas in the US.\", name=\"Lance\"))\n",
    "\n",
    "for m in messages:\n",
    "    m.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ca48df0-b639-4ff1-a777-ffe2185d991e",
   "metadata": {},
   "source": [
    "## Chat Models\n",
    "\n",
    "[Chat models](https://python.langchain.com/v0.2/docs/concepts/#chat-models) can use a sequence of message as input and support message types, as discussed above.\n",
    "\n",
    "There are [many](https://python.langchain.com/v0.2/docs/concepts/#chat-models) to choose from! Let's work with OpenAI. \n",
    "\n",
    "Let's check that your `OPENAI_API_KEY` is set and, if not, you will be asked to enter it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "2652d5ec-7602-4220-bc6e-b90783ab287b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ceae53d4-14f5-4bf3-a953-cc465240f5b5",
   "metadata": {},
   "source": [
    "We can load a chat model and invoke it with our list of messages.\n",
    "\n",
    "We can see that the result is an `AIMessage` with specific `response_metadata`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "95b99ad4-5753-49d3-a916-a9e949722c01",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "langchain_core.messages.ai.AIMessage"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "llm = ChatOpenAI(model=\"gpt-4o\")\n",
    "result = llm.invoke(messages)\n",
    "type(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "88d60338-c892-4d04-a83f-878de4a76a6a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='One of the best places to see orcas in the United States is the San Juan Islands in Washington State. The waters around these islands, particularly near the town of Friday Harbor on San Juan Island, are well-known for orca sightings. \\n\\nThe Southern Resident Orca population frequents the Salish Sea, which includes the San Juan Islands, especially during the summer months when they hunt for salmon. Several tour companies offer whale-watching excursions, providing guided experiences to observe these incredible marine mammals in their natural habitat.\\n\\nAdditionally, places like Lime Kiln Point State Park, also known as \"Whale Watch Park,\" provide land-based viewing opportunities. It is one of the few spots in the world where you can potentially see orcas from the shore.\\n\\nRemember to book your tour in advance and choose responsible, eco-friendly operators who prioritize the safety and well-being of the wildlife.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 176, 'prompt_tokens': 67, 'total_tokens': 243, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_f9f4fb6dbf', 'finish_reason': 'stop', 'logprobs': None}, id='run-c39a1c93-abdf-4d10-bac5-f466666b6ae8-0', usage_metadata={'input_tokens': 67, 'output_tokens': 176, 'total_tokens': 243, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c3a29654-6b8e-4eda-9cec-22fabb9b8620",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'token_usage': {'completion_tokens': 176,\n",
       "  'prompt_tokens': 67,\n",
       "  'total_tokens': 243,\n",
       "  'completion_tokens_details': {'accepted_prediction_tokens': 0,\n",
       "   'audio_tokens': 0,\n",
       "   'reasoning_tokens': 0,\n",
       "   'rejected_prediction_tokens': 0},\n",
       "  'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}},\n",
       " 'model_name': 'gpt-4o-2024-08-06',\n",
       " 'system_fingerprint': 'fp_f9f4fb6dbf',\n",
       " 'finish_reason': 'stop',\n",
       " 'logprobs': None}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result.response_metadata"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4718bd5c-5314-4405-a164-f1fe912ae306",
   "metadata": {},
   "source": [
    "## Tools\n",
    "\n",
    "Tools are useful whenever you want a model to interact with external systems.\n",
    "\n",
    "External systems (e.g., APIs) often require a particular input schema or payload, rather than natural language. \n",
    "\n",
    "When we bind an API, for example, as a tool we given the model awareness of the required input schema.\n",
    "\n",
    "The model will choose to call a tool based upon the natural language input from the user. \n",
    "\n",
    "And, it will return an output that adheres to the tool's schema. \n",
    "\n",
    "[Many LLM providers support tool calling](https://python.langchain.com/v0.1/docs/integrations/chat/) and [tool calling interface](https://blog.langchain.dev/improving-core-tool-interfaces-and-docs-in-langchain/) in LangChain is simple. \n",
    " \n",
    "You can simply pass any Python `function` into `ChatModel.bind_tools(function)`.\n",
    "\n",
    "![Screenshot 2024-08-19 at 7.46.28 PM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbab08dc1c17a7a57f9960_chain2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17a942b1",
   "metadata": {},
   "source": [
    "Let's showcase a simple example of tool calling!\n",
    " \n",
    "The `multiply` function is our tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "928faf56-1a1a-4c5f-b97d-bd64d8e166d1",
   "metadata": {},
   "outputs": [],
   "source": [
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply a and b.\n",
    "\n",
    "    Args:\n",
    "        a: first int\n",
    "        b: second int\n",
    "    \"\"\"\n",
    "    return a * b\n",
    "\n",
    "llm_with_tools = llm.bind_tools([multiply])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a3f9dba",
   "metadata": {},
   "source": [
    "If we pass an input - e.g., `\"What is 2 multiplied by 3\"` - we see a tool call returned. \n",
    "\n",
    "The tool call has specific arguments that match the input schema of our function along with the name of the function to call.\n",
    "\n",
    "```\n",
    "{'arguments': '{\"a\":2,\"b\":3}', 'name': 'multiply'}\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "9edbe13e-cc72-4685-ac97-2ebb4ceb2544",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_call = llm_with_tools.invoke([HumanMessage(content=f\"What is 2 multiplied by 3\", name=\"Lance\")])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "a78178cb-fa43-45b5-be5e-5a22bda5a5e7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'name': 'multiply',\n",
       "  'args': {'a': 2, 'b': 3},\n",
       "  'id': 'call_ogcv27ZGAgPTMWAxwJVslfBe',\n",
       "  'type': 'tool_call'}]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tool_call.tool_calls"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8e0c5b0b-a39d-43d3-9349-47ecdce5d508",
   "metadata": {},
   "outputs": [],
   "source": [
    "#So even without connection the graph, if we have binded our llm to the tools list, we can get tools_condition state\n",
    "#and we can get tool calls. We just cant actually execute the call and get the result -- we need to construct the graph for that"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21c10f9a-2372-486b-9305-55b7c41ecd6e",
   "metadata": {},
   "source": [
    "## Using messages as state\n",
    "\n",
    "With these foundations in place, we can now use [`messages`](https://python.langchain.com/v0.2/docs/concepts/#messages) in our graph state.\n",
    "\n",
    "Let's define our state, `MessagesState`, as a `TypedDict` with a single key: `messages`.\n",
    "\n",
    "`messages` is simply a list of messages, as we defined above (e.g., `HumanMessage`, etc)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "3699dd5c-398c-43c7-b496-fd87e55e11ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langchain_core.messages import AnyMessage\n",
    "\n",
    "class MessagesState(TypedDict):\n",
    "    messages: list[AnyMessage]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "31e51c31-7762-4088-a5c7-b9355382425d",
   "metadata": {},
   "outputs": [],
   "source": [
    "#Above shows that MessageState is just a list of messages"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "211cba3e-ebba-4b91-a539-1cbc28b4a40e",
   "metadata": {},
   "source": [
    "## Reducers\n",
    "\n",
    "Now, we have a minor problem! \n",
    "\n",
    "As we discussed, each node will return a new value for our state key `messages`.\n",
    "\n",
    "But, this new value [will override](https://langchain-ai.github.io/langgraph/concepts/low_level/#reducers) the prior `messages` value.\n",
    " \n",
    "As our graph runs, we want to **append** messages to our `messages` state key.\n",
    " \n",
    "We can use [reducer functions](https://langchain-ai.github.io/langgraph/concepts/low_level/#reducers) to address this.\n",
    "\n",
    "Reducers allow us to specify how state updates are performed.\n",
    "\n",
    "If no reducer function is specified, then it is assumed that updates to the key should *override it* as we saw before.\n",
    " \n",
    "But, to append messages, we can use the pre-built `add_messages` reducer.\n",
    "\n",
    "This ensures that any messages are appended to the existing list of messages.\n",
    "\n",
    "We simply need to annotate our `messages` key with the `add_messages` reducer function as metadata."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "6b33eb72-3197-4870-b9a3-0da8056c40c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Annotated\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "class MessagesState(TypedDict):\n",
    "    messages: Annotated[list[AnyMessage], add_messages]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3663e574-ba15-46be-a37c-48c8052d693b",
   "metadata": {},
   "source": [
    "Since having a list of messages in graph state is so common, LangGraph has a pre-built [`MessagesState`](https://langchain-ai.github.io/langgraph/concepts/low_level/#messagesstate)! \n",
    "\n",
    "`MessagesState` is defined: \n",
    "\n",
    "* With a pre-build single `messages` key\n",
    "* This is a list of `AnyMessage` objects \n",
    "* It uses the `add_messages` reducer\n",
    "\n",
    "We'll usually use `MessagesState` because it is less verbose than defining a custom `TypedDict`, as shown above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "9ab516ee-eab1-4856-8210-99f1fe499672",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.graph import MessagesState\n",
    "\n",
    "class MessagesState(MessagesState):\n",
    "    # Add any keys needed beyond messages, which is pre-built \n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36b0fff7-60a2-4582-8f12-3a3ab6633d6c",
   "metadata": {},
   "source": [
    "To go a bit deeper, we can see how the `add_messages` reducer works in isolation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "23ffea76-16a5-4053-a1bc-91e0101d91dc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='Hello! How can I assist you?', additional_kwargs={}, response_metadata={}, name='Model', id='a6125ef3-be4f-4437-a9b5-863b8cc4f9b8'),\n",
       " HumanMessage(content=\"I'm looking for information on marine biology.\", additional_kwargs={}, response_metadata={}, name='Lance', id='838b717d-bab1-42ea-9b58-20a0e22b2db7'),\n",
       " AIMessage(content='Sure, I can help with that. What specifically are you interested in?', additional_kwargs={}, response_metadata={}, name='Model', id='80d1e4e0-a37e-49bd-8d36-f02052a508e4')]"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Initial state\n",
    "initial_messages = [AIMessage(content=\"Hello! How can I assist you?\", name=\"Model\"),\n",
    "                    HumanMessage(content=\"I'm looking for information on marine biology.\", name=\"Lance\")\n",
    "                   ]\n",
    "\n",
    "# New message to add\n",
    "new_message = AIMessage(content=\"Sure, I can help with that. What specifically are you interested in?\", name=\"Model\")\n",
    "\n",
    "# Test\n",
    "add_messages(initial_messages , new_message) #essentially just appends the new_message to the initial_messages list"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "485adccc-f262-49dd-af4f-a30e9b6a48e2",
   "metadata": {},
   "source": [
    "## Our graph\n",
    "\n",
    "Now, lets use `MessagesState` with a graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "b5306639-7e6a-44be-8471-8d2631701cfb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "    \n",
    "# Node\n",
    "# parameters are MessagesState, which as we saw are just a list of messages with metadata\n",
    "# returns the result upon invoking the given tool-bound llm with the list of messages from MessagesState\n",
    "def tool_calling_llm(state: MessagesState):\n",
    "    return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n",
    "\n",
    "# Build graph\n",
    "builder = StateGraph(MessagesState) #creates __start__ node and __end__ node (given by langgraph's StateGraph model)\n",
    "builder.add_node(\"tool_calling_llm\", tool_calling_llm) #creates too_calling_llm node using our provided def\n",
    "builder.add_edge(START, \"tool_calling_llm\") #solid edge from START (__start__) to our tool_calling_llm\n",
    "builder.add_edge(\"tool_calling_llm\", END) #solid edge from tool_calling_llm to __end__ node\n",
    "graph = builder.compile() #compiles agentic model\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8909771-7786-47d6-a53d-6bbc3b365737",
   "metadata": {},
   "source": [
    "If we pass in `Hello!`, the LLM responds without any tool calls."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "983e2487-c0a5-40a2-afbc-aa53ff49fefc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Hello!\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hello! How can I assist you today?\n"
     ]
    }
   ],
   "source": [
    "messages = graph.invoke({\"messages\": HumanMessage(content=\"Hello!\")})\n",
    "for m in messages['messages']:\n",
    "    m.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3588688b-efd9-4dbc-abf2-7903e3ef89ba",
   "metadata": {},
   "source": [
    "The LLM chooses to use a tool when it determines that the input or task requires the functionality provided by that tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "7fe8b042-ecc8-426f-995e-cc1bbaf7cacc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Multiply 2 and 3\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  multiply (call_MvXaERtwD4rW6FHP93aNlIJZ)\n",
      " Call ID: call_MvXaERtwD4rW6FHP93aNlIJZ\n",
      "  Args:\n",
      "    a: 2\n",
      "    b: 3\n"
     ]
    }
   ],
   "source": [
    "messages = graph.invoke({\"messages\": HumanMessage(content=\"Multiply 2 and 3\")})\n",
    "for m in messages['messages']:\n",
    "    m.pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "638ba14c-d355-4294-95d8-483eb44b218c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='Multiply 2 and 3' additional_kwargs={} response_metadata={} id='fc391a81-ad12-4b51-89c5-7ea4547a3372'\n",
      "content='' additional_kwargs={'tool_calls': [{'id': 'call_MvXaERtwD4rW6FHP93aNlIJZ', 'function': {'arguments': '{\"a\":2,\"b\":3}', 'name': 'multiply'}, 'type': 'function'}], 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 18, 'prompt_tokens': 58, 'total_tokens': 76, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_f9f4fb6dbf', 'finish_reason': 'tool_calls', 'logprobs': None} id='run-85b16f38-d225-477a-bc6c-e6a177f53065-0' tool_calls=[{'name': 'multiply', 'args': {'a': 2, 'b': 3}, 'id': 'call_MvXaERtwD4rW6FHP93aNlIJZ', 'type': 'tool_call'}] usage_metadata={'input_tokens': 58, 'output_tokens': 18, 'total_tokens': 76, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}\n"
     ]
    }
   ],
   "source": [
    "for m in messages['messages']:\n",
    "    print(m)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "311fbae3",
   "metadata": {},
   "outputs": [],
   "source": [
    "#FINISHED LESSON\n",
    "#Learned\n",
    "#what MessagesState is and how tool_call works without the full model\n",
    "#note that we dont actually get the output from the model because we dont have an edge leading to an assistant that can give us the output"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
